/*----------------------------------------------------------------------------
 *      RL-ARM - UART
 *----------------------------------------------------------------------------
 *      Name:    RTX_UART.c
 *      Purpose: UART Generic Layer Driver
 *      Rev.:    V4.70
 *----------------------------------------------------------------------------
 *      This code is part of the RealView Run-Time Library.
 *      Copyright (c) 2004-2013 KEIL - An ARM Company. All rights reserved.
 *---------------------------------------------------------------------------*/

#include "RTL.h"      /* RTX kernel functions & defines      */
#include "RTX_UART.h" /* UART Generic functions & defines     */

#pragma diag_suppress 550

/* Declare memory pool for UART messages, both transmit and receive           */
U32 UART_msg_pool[((sizeof(UART_msg_t) + 3) / 4) * (UART_CTRL_MAX_NUM * (UART_TX_BUF_MAX + UART_RX_BUF_MAX)) + 3];

/* Declare mailbox, for UART transmit messages                                */
U32 UART_MBX_tx_ctrl[UART_CTRL_MAX_NUM][4 + UART_TX_BUF_MAX];

/* Declare mailbox, for UART receive messages                                 */
U32 UART_MBX_rx_ctrl[UART_CTRL_MAX_NUM][4 + UART_RX_BUF_MAX];

/* Semaphores used for protecting writing to UART hardware                    */
OS_SEM UART_wr_sem[UART_CTRL_MAX_NUM];

/*----------------------------------------------------------------------------
 *      UART RTX Generic Driver Functions
 *----------------------------------------------------------------------------
 *  Functions implemented in this module:
 *           UART_ERROR UART_mem_init  (void);
 *           UART_ERROR UART_setup     (void)
 *           UART_ERROR UART_init      (U32 ctrl, U32 baudrate)
 *    static UART_ERROR UART_push      (U32 ctrl, UART_msg *msg, U16 timeout)
 *           UART_ERROR UART_send      (U32 ctrl, UART_msg *msg, U16 timeout)
 *           UART_ERROR UART_request   (U32 ctrl, UART_msg *msg, U16 timeout)
 *           UART_ERROR UART_set       (U32 ctrl, UART_msg *msg, U16 timeout)
 *    static UART_ERROR UART_pull      (U32 ctrl, UART_msg *msg, U16 timeout)
 *           UART_ERROR UART_receive   (U32 ctrl, UART_msg *msg, U16 timeout)
 *           UART_ERROR UART_rx_object (U32 ctrl, U32 ch, U32 id, U32 object_para)
 *           UART_ERROR UART_tx_object (U32 ctrl, U32 ch,         U32 object_para)
 *---------------------------------------------------------------------------*/

/*--------------------------- UART_init --------------------------------------
 *
 *  The first time this function is called initialize the memory pool for
 *  UART messages and setup UART controllers hardware
 *
 *  Initialize mailboxes for UART messages and initialize UART controller
 *
 *  Parameter:  ctrl:       Index of the hardware UART controller (1 .. x)
 *              baudrate:   Baudrate
 *
 *  Return:     UART_ERROR:  Error code
 *---------------------------------------------------------------------------*/

UART_ERROR UART_init(U32 ctrl, U32 baudrate)
{
    static U8 first_run_flag = 0;
    UART_ERROR error_code;
    U32 ctrl0 = ctrl - 1; /* Controller index 0 .. x-1 */

    /* Initialize the Semaphore before the first use */
    os_sem_init(UART_wr_sem[ctrl0], 1);

    /* When function is called for the first time it will initialize and setup
     all of the resources that are common to UART functionality */
    if (first_run_flag == 0)
    {
        first_run_flag = 1;
        if (_init_box(UART_msg_pool, sizeof(UART_msg_pool), sizeof(UART_msg_t)) == 1)
        {
            return UART_MEM_POOL_INIT_ERROR;
        }
    }

    os_mbx_init(UART_MBX_tx_ctrl[ctrl0], sizeof(UART_MBX_tx_ctrl[ctrl0]));
    os_mbx_init(UART_MBX_rx_ctrl[ctrl0], sizeof(UART_MBX_rx_ctrl[ctrl0]));

    error_code = UART_hw_setup(ctrl);
    if (error_code != UART_OK)
    {
        return error_code;
    }

    if (UART_hw_init(ctrl, baudrate) != UART_OK)
    {
        return UART_NOT_IMPLEMENTED_ERROR;
    }

    return UART_hw_start(ctrl);
}

/*--------------------------- UART_push --------------------------------------
 *
 *  Send UART_msg if hardware is free for sending, otherwise push message to
 *  message queue to be sent when hardware becomes free
 *
 *  Parameter:  ctrl:       Index of the hardware UART controller (1 .. x)
 *              msg:        Pointer to UART message to be sent
 *              timeout:    Timeout value for message sending
 *
 *  Return:     UART_ERROR:  Error code
 *---------------------------------------------------------------------------*/

static UART_ERROR UART_push(U32 ctrl, UART_msg_t* msg, U16 timeout)
{
    UART_msg_t* ptrmsg;
    U32 ctrl0 = ctrl - 1; /* Controller index 0 .. x-1           */

    /* Transmit hardware free for send */
    if (UART_hw_tx_empty(ctrl) == UART_OK)
    {
        UART_hw_wr(ctrl, msg); /* Send message */
    }
    else
    {
        /* If hardware for sending is busy temporary
         save the message to send mailbox if there is room for it */
        ptrmsg = _alloc_box(UART_msg_pool);
        if (ptrmsg == NULL)
        {
            return UART_ALLOC_MEM_ERROR;
        }
        *ptrmsg = *msg;

        /* If message hasn't been sent but timeout expired, deallocate memory  */
        if (os_mbx_send(UART_MBX_tx_ctrl[ctrl0], ptrmsg, timeout) == OS_R_TMO)
        {
            if (_free_box(UART_msg_pool, ptrmsg) == 1)
            {
                return UART_DEALLOC_MEM_ERROR;
            }

            return UART_TIMEOUT_ERROR;
        }
        else
        {
            /* Check once again if transmit hardware is ready for transmission   */
            if (UART_hw_tx_empty(ctrl) == UART_OK)
            {
                /* Transmit hw free for send */
                if (os_mbx_wait(UART_MBX_tx_ctrl[ctrl0], (void**)&ptrmsg, 0) == OS_R_TMO)
                {
                    os_sem_send(UART_wr_sem[ctrl0]); /* Return a token back to semaphore  */
                    return UART_OK;                  /* Message was sent from IRQ already */
                }
                if (_free_box(UART_msg_pool, ptrmsg) == 1)
                {
                    os_sem_send(UART_wr_sem[ctrl0]); /* Return a token back to semaphore  */
                    return UART_DEALLOC_MEM_ERROR;
                }
                /* Send message */
                UART_hw_wr(ctrl, msg);
            }
        }
    }
    return UART_OK;
}

/*--------------------------- UART_send --------------------------------------
 *
 *  Send DATA FRAME message, see UART_push function comment
 *
 *  Parameter:  ctrl:       Index of the hardware UART controller (1 .. x)
 *              msg:        Pointer to UART message to be sent
 *              timeout:    Timeout value for message sending
 *
 *  Return:     UART_ERROR:  Error code
 *---------------------------------------------------------------------------*/

UART_ERROR UART_send(U32 ctrl, UART_msg_t* msg, U16 timeout)
{
    return (UART_push(ctrl, msg, timeout));
}

/*--------------------------- UART_pull --------------------------------------
 *
 *  Pull first received and unread UART_msg from receiving message queue
 *
 *  Parameter:  ctrl:       Index of the hardware UART controller (1 .. x)
 *              msg:        Pointer where UART message will be read
 *              timeout:    Timeout value for message receiving
 *
 *  Return:     UART_ERROR:  Error code
 *---------------------------------------------------------------------------*/

static UART_ERROR UART_pull(U32 ctrl, UART_msg_t* msg, U16 timeout)
{
    UART_msg_t* ptrmsg;
    U32 ctrl0 = ctrl - 1; /* Controller index 0..x - 1 */

    /* Wait for received message in mailbox */
    if (os_mbx_wait(UART_MBX_rx_ctrl[ctrl0], (void**)&ptrmsg, timeout) == OS_R_TMO)
    {
        return UART_TIMEOUT_ERROR;
    }

    /* Copy received message from mailbox to address given in function parameter msg */
    *msg = *ptrmsg;

    /* Free box where message was kept */
    if (_free_box(UART_msg_pool, ptrmsg) == 1)
    {
        return UART_DEALLOC_MEM_ERROR;
    }

    return UART_OK;
}

/*--------------------------- UART_receive -----------------------------------
 *
 *  Read received message, see UART_pull function comment
 *
 *  Parameter:  ctrl:       Index of the hardware UART controller (1 .. x)
 *              msg:        Pointer where UART message will be read
 *              timeout:    Timeout value for message receiving
 *
 *  Return:     UART_ERROR:  Error code
 *---------------------------------------------------------------------------*/

UART_ERROR UART_receive(U32 ctrl, UART_msg_t* msg, U16 timeout)
{
    return (UART_pull(ctrl, msg, timeout));
}

/*----------------------------------------------------------------------------
 * end of file
 *---------------------------------------------------------------------------*/
